package com.ujson.base.domain.repository.impl;

import com.ujson.base.common.exception.GlobalException;
import com.ujson.base.domain.entity.Aggregate;
import com.ujson.base.domain.entity.EntityDiff;
import com.ujson.base.domain.entity.Identifier;
import com.ujson.base.domain.utils.DiffUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class DBContext<T extends Aggregate<ID>, ID extends Identifier> {
    private final Class<T> targetClass;
    private final Map<ID, T> aggregateMap = new HashMap<>();

    public DBContext(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    public void attach(T aggregate) {
        if (Objects.nonNull(aggregate.getId())) {
            if (!aggregateMap.containsKey(aggregate.getId())) {
                this.merge(aggregate);
            }
        }
    }

    public void detach(T aggregate) {
        if (Objects.nonNull(aggregate.getId())) {
            aggregateMap.remove(aggregate.getId());
        }
    }

    public EntityDiff detectChanges(T aggregate) throws IllegalAccessException {
        if (aggregate.getId() == null) {
            return EntityDiff.EMPTY;
        }
        T snapshot = aggregateMap.get(aggregate.getId());
        if (snapshot == null) {
            attach(aggregate);
        }
        return DiffUtils.diff(snapshot, aggregate);
    }

    public T find(ID id) {
        return aggregateMap.get(id);
    }

    public void merge(T aggregate) {
        if (Objects.nonNull(aggregate.getId())) {
            aggregateMap.put(aggregate.getId(), aggregate);
        }
    }

    public void setId(T aggregate, ID id) {
        try {
            FieldUtils.writeField(aggregate, "id", id);
        } catch (IllegalAccessException e) {
            throw new GlobalException(e);
        }
    }
}
